package cn.migu.music.hangupsms.service.sftp;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.*;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import com.jcraft.jsch.*;

import cn.migu.music.common.exception.BusinessException;
import cn.migu.music.common.exception.SystemException;
import cn.migu.music.common.utils.LogUtils;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class SftpService {
    @SuppressWarnings("all")
    public ChannelSftp getsftp(String host, int port, String userName, String pwd) {
        Session session;
        try {
            JSch jSch = new JSch();
            session = jSch.getSession(userName, host, port);
            session.setTimeout(10000);

            if (!StringUtils.isEmpty(pwd)) {
                session.setPassword(pwd);
            }
            Properties config = new Properties();
            config.put("StrictHostKeyChecking", "no");

            session.setConfig(config);
            session.connect();

            Channel channel = session.openChannel("sftp");
            channel.connect();

            ChannelSftp sftp = (ChannelSftp) channel;
            LogUtils.info(log, "连接到sftp成功");
            return sftp;
        } catch (Exception e) {
            LogUtils.error(e, log, "连接到sftp失败");
            throw BusinessException.newInstance("连接到sftp失败");
        }
    }

    private void assertsftp(ChannelSftp sftp) {
        if (sftp == null) {
            throw SystemException.newInstance("sftpsftp is null");
        }
    }

    public void disconnect(ChannelSftp sftp) {
        assertsftp(sftp);
        Session session = null;
        try {
            session = sftp.getSession();
            if (sftp.isConnected()) {
                sftp.disconnect();
            }
        } catch (JSchException e) {
            LogUtils.error(e, log, "关闭sftp失败");
        } finally {
            if (session != null) {
                session.disconnect();
            }
        }
    }

    public boolean isFileExist(String fileName, ChannelSftp sftp) {
        assertsftp(sftp);
        try {
            sftp.lstat(fileName);
        } catch (SftpException e) {
            if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
                return false;
            } else {
                LogUtils.error(e, log, "sftp lstat file error:%s", fileName);
                throw SystemException.newInstance("ftp检查文件是否存在失败:" + fileName, e);
            }
        }
        return true;
    }

    public void createDir(ChannelSftp sftp, String createPath) {
        assertsftp(sftp);
        try {
            String currentWorkDir = sftp.pwd();
            String[] pathArray = StringUtils.split(createPath, File.separator);
            StringBuilder filePath = new StringBuilder(StringUtils.join(sftp.getHome(), File.separator));
            for (String path : pathArray) {
                if (StringUtils.isBlank(path)) {
                    continue;
                }
                filePath.append(path).append(File.separator);
                if (!isExistDir(filePath.toString(), sftp)) {
                    // 建立目录
                    sftp.mkdir(filePath.toString());
                }
                // 进入并设置为当前目录
                sftp.cd(filePath.toString());
            }
            sftp.cd(currentWorkDir);
        } catch (SftpException e) {
            LogUtils.error(e, log, "sftp mkdir error，%s", createPath);
            throw SystemException.newInstance("ftp创建目录失败：" + createPath, e);
        }
    }

    public boolean isExistDir(String directory, ChannelSftp sftp) {
        assertsftp(sftp);
        try {
            SftpATTRS sftpAttrS = sftp.lstat(directory);
            return sftpAttrS.isDir();
        } catch (SftpException e) {
            if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
                return false;
            } else {
                LogUtils.error(e, log, "sftp lssta dir error：%s", directory);
                throw SystemException.newInstance("ftp检查目录是否存在失败:" + directory, e);
            }
        }
    }

    public void uploadFile(ChannelSftp sftp, String localFileName, String remoteFileName) {
        assertsftp(sftp);
        try {
            sftp.put(localFileName, remoteFileName);
        } catch (SftpException e) {
            LogUtils.error(e, log, "sftp put error:%s", remoteFileName);
            throw SystemException.newInstance("ftp上传文件失败：" + remoteFileName, e);
        }
    }

    /**
     * 上传文件
     * 
     * @param sftp
     * @param relativePath
     *            上传的路径
     * @param fileName
     *            文件名称
     * @param inputStream
     *            文件信息
     */
    public void uploadFile(ChannelSftp sftp, String relativePath, String fileName, InputStream inputStream) {
        assertsftp(sftp);
        try {
            if (isExistDir(relativePath, sftp)) {
                sftp.put(inputStream, relativePath + "/" + fileName);
            }
        } catch (SftpException e) {
            LogUtils.error(e, log, "sftp put error:%s/%s", relativePath, fileName);
            throw SystemException.newInstance("ftp上传文件失败：" + relativePath + "/" + fileName, e);
        }
    }

    public String fileInfo(String fileName, ChannelSftp sftp) {
        assertsftp(sftp);
        try {
            SftpATTRS attrs = sftp.lstat(fileName);
            return attrs.toString();
        } catch (SftpException e) {
            LogUtils.error(e, log, "lstat:%s", fileName);
            return e.id + "," + e.getMessage();
        }
    }

    @SuppressWarnings("all")
    public List<String> ls(String path, ChannelSftp sftp) {
        assertsftp(sftp);
        try {
            Vector vector = sftp.ls(path);
            List<String> list = new ArrayList<>();
            for (Object o : vector) {
                ChannelSftp.LsEntry entry = (ChannelSftp.LsEntry) o;
                if (StringUtils.equals(entry.getFilename(), ".") || StringUtils.equals(entry.getFilename(), "..")) {
                    continue;
                }
                list.add(entry.getFilename());
            }
            return list;
        } catch (SftpException e) {
            LogUtils.error(e, log, "ls:%s", path);
            throw SystemException.newInstance("ls：" + path, e);
        }
    }

    /**
     * 获取目录下的文件信息
     * 
     * @param path
     *            目录信息
     * @param sftp
     * @return
     */
    public List<ChannelSftp.LsEntry> lsVector(String path, ChannelSftp sftp) {
        assertsftp(sftp);
        try {
            Iterator<ChannelSftp.LsEntry> vector = sftp.ls(path).iterator();
            List<ChannelSftp.LsEntry> list = new ArrayList<>();
            while (vector.hasNext()) {
                list.add(vector.next());
            }
            return list;
        } catch (SftpException e) {
            LogUtils.error(e, log, "ls:%s", path);
            throw SystemException.newInstance("ls：" + path, e);
        }
    }

    /**
     * 下载到本地
     * 
     * @param remoteFile
     * @param sftp
     */
    public void download(String remoteFile, String localFile, ChannelSftp sftp) {
        assertsftp(sftp);
        // 判断当前文件是全量还是增量
        try {
            File lf = new File(localFile);
            // 文件夹目录不存在则创建目录
            if (!lf.getParentFile().exists()) {
                lf.getParentFile().mkdirs();
            }
            FileOutputStream os = new FileOutputStream(lf);
            try {
                sftp.get(remoteFile, os);
            } finally {
                os.flush();
                os.close();
            }
        } catch (Exception e) {
            LogUtils.error(e, log, "下载文件失败：" + remoteFile);
            throw SystemException.newInstance("下载失败,文件目录找不到!", e);
        }

    }

    /**
     * 下载文件
     * 
     * @param filePath
     *            文件完整路径和名称
     * @param sftp
     * @return
     */
    public byte[] download(String filePath, ChannelSftp sftp) {
        assertsftp(sftp);
        // 判断当前文件是全量还是增量
        try {
            InputStream inputStream = sftp.get(filePath);
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            int n;
            byte[] data = new byte[1024];
            try {
                while ((n = inputStream.read(data, 0, data.length)) != -1) {
                    buffer.write(data, 0, n);
                }
                buffer.flush();
            } finally {
                inputStream.close();
                buffer.close();
            }
            return buffer.toByteArray();
        } catch (Exception e) {
            LogUtils.error(e, log, "下载文件失败：" + filePath);
            throw SystemException.newInstance("下载失败,文件目录找不到!", e);
        }

    }

    /**
     * 删除文件
     *
     * @param remoteFile
     * @param sftp
     */
    public void delFile(String remoteFile, ChannelSftp sftp) {
        assertsftp(sftp);
        try {
            sftp.rm(remoteFile);
        } catch (Exception e) {
            LogUtils.error(e, log, "删除文件失败：" + remoteFile);
            throw SystemException.newInstance("删除文件失败:" + e.getMessage(), e);
        }

    }
}
